﻿namespace Pyramid
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    internal static class Util
    {
        private readonly static Dictionary<char, int> CharacterValueLookup = new Dictionary<char, int> {
            {'4', 4},
            {'3', 3},
            {'2', 2},
            {'1', 1},
            {'0', 0},
            {'Z', -1},
            {'Y', -2},
            {'X', -3},
            {'W', -4}};

        internal const string TERNARY_FORMAT_PREFIX = "0T";

        internal const string NONARY_FORMAT_PREFIX = "0N";

        internal static int CompareTo<T>(T left, object right)
            where T : IComparable<T>
        {
            if (right is T)
            {
                return left.CompareTo((T)right);
            }
            else
            {
                throw new ArgumentException("Object is not a " + typeof(T).ToString());
            }
        }

        [Conditional("DEBUG")]
        internal static void AssertRange(long value, long min, long max)
        {
            Debug.Assert(value <= max, "This type can only have values below " + max + ".");
            Debug.Assert(value >= min, "This type can only have values above " + min + ".");
        }

        internal static Trivalent[] ToTritArray(long value, int trits)
        {
            //Keeps a running track of the value yet to be encoded
            long accumulator = value;

            //Buffer for the trit values
            var tritArray = new Trivalent[trits];

            for (int tritIndex = trits - 1; tritIndex >= 0; tritIndex--)
            {
                //The value of a trit at this location in the tryte
                long tritValue = (long)Math.Pow(3, tritIndex);

                long sign = 0;

                if (tritValue < (Math.Abs(accumulator) * 2))
                {
                    sign = (long)Math.Sign(accumulator);
                }

                //Remove the value of this trit from the value
                //yet to be encoded
                accumulator -= (long)(sign * tritValue);

                //Convert the sign (which equates to the trit value of
                //+1, 0 or -1) to a Trivalent
                tritArray[tritIndex] = (Trivalent)sign;
            }

            return tritArray;
        }

        internal static long ParseTernaryString(string s)
        {
            s = s.Substring(TERNARY_FORMAT_PREFIX.Length).ToUpperInvariant();

            long result = 0;

            for (int tritIndex = 0; tritIndex < s.Length; tritIndex++)
            {
                long tritValue = (long)Math.Pow(3, tritIndex);

                char tritCharacter = s[s.Length - (tritIndex + 1)];

                long multiplier = CharacterValueLookup[tritCharacter];

                result += tritValue * multiplier;
            }

            return result;
        }

        internal static long ParseNonaryString(string s)
        {
            s = s.Substring(NONARY_FORMAT_PREFIX.Length).ToUpperInvariant();

            long result = 0;

            for (int digitIndex = 0; digitIndex < s.Length; digitIndex++)
            {
                long digitValue = (long)Math.Pow(9, digitIndex);

                char digitCharacter = s[s.Length - (digitIndex + 1)];

                long multiplier = CharacterValueLookup[digitCharacter];

                result += digitValue * multiplier;
            }

            return result;
        }

        public static long Trim(long value, long maxValue, long minValue)
        {
            long range = (maxValue - minValue) + 1;

            while (value > maxValue)
            {
                value -= range;
            }

            while (value < minValue)
            {
                value += range;
            }

            return value;
        }
    }
}